<!-- 📚📚📚 Pro-Table 文档: https://juejin.cn/post/7094890833064755208 -->
<!-- 💢💢💢 后期会重构 Pro-Table 组件，使用 v-bind 属性透传 -->

<template>
	<!-- 查询表单 -->
	<ProSearch v-model="searchParam" :search-items="searchColumns" v-show="isShowSearch" @reset="reset" @search="search">
		<!-- 递归传递插槽！！！ -->
		<template v-for="slot in Object.keys($slots)" #[slot]="scope">
			<slot :name="slot" v-bind="scope" />
		</template>
	</ProSearch>
	<div :class="['card', 'table', { 'no-full-table': isFullTable }]">
		<!-- 表格头部 操作按钮 -->
		<div class="table-header">
			<div class="header-button-lf">
				<slot name="tableHeader" :selectedListIds="selectedListIds" :selectList="selectedList" :isSelected="isSelected"></slot>
			</div>
			<div class="header-button-ri" v-if="toolButton">
				<el-tooltip class="box-item" effect="dark" content="刷新" placement="top">
					<el-button :icon="Refresh" circle @click="getTableList"> </el-button>
				</el-tooltip>
				<el-tooltip class="box-item" effect="dark" content="打印" placement="top">
					<el-button :icon="Printer" circle @click="handlePrint"> </el-button>
				</el-tooltip>
				<el-tooltip class="box-item" effect="dark" content="列设置" placement="top">
					<el-button ref="popoverColSettingRef" :icon="Operation" circle @click="openPopoverColSetting"> </el-button>
				</el-tooltip>
				<el-button :icon="Search" circle v-if="searchColumns.length" @click="isShowSearch = !isShowSearch"> </el-button>
			</div>
		</div>
		<!-- 表格主体 -->
		<el-table
			ref="tableRef"
			v-loading="loading"
			:data="tableData"
			:border="border"
			@selection-change="selectionChange"
			:row-key="getRowKeys"
			:stripe="stripe"
			:tree-props="{ children: childrenName }"
		>
			<template v-for="(item, index) in tableColumns" :key="index">
				<!-- selection || index -->
				<el-table-column
					v-if="(item.type == 'selection' || item.type == 'index') && item.isShow && item.colShow"
					:type="item.type"
					:reserve-selection="item.type == 'selection'"
					:label="item.label"
					:width="item.width"
					:min-width="item.minWidth"
					:fixed="item.fixed"
					:align="item.align ?? 'center'"
				>
				</el-table-column>
				<!-- expand（展开自定义详细信息，请使用作用域插槽） -->
				<el-table-column
					v-if="item.type == 'expand' && item.isShow && item.colShow"
					:type="item.type"
					:label="item.label"
					:width="item.width"
					:min-width="item.minWidth"
					:fixed="item.fixed"
					:align="item.align ?? 'center'"
					v-slot="scope"
				>
					<slot :name="item.type" v-bind="scope"></slot>
				</el-table-column>
				<!-- other -->
				<el-table-column
					v-if="!item.type && item.prop && item.isShow && item.colShow"
					:prop="item.prop"
					:label="item.label"
					:width="item.width"
					:min-width="item.minWidth"
					:sortable="item.sortable"
					:show-overflow-tooltip="item.prop !== 'operation'"
					:resizable="true"
					:fixed="item.fixed"
					:align="item.align ?? 'left'"
				>
					<!-- 自定义 header (使用组件渲染 tsx 语法) -->
					<template #header v-if="item.renderHeader">
						<component :is="item.renderHeader" :row="item"> </component>
					</template>

					<!-- 自定义配置每一列 slot（使用作用域插槽） -->
					<template #default="scope">
						<slot :name="item.prop" v-bind="scope">
							<!-- tag 标签（自带格式化内容） -->
							<el-tag v-if="item.tag" :type="filterEnum(scope.row[item.prop!], item.enum!, item.searchProps, 'tag')">
								{{
									item.enum?.length
										? filterEnum(scope.row[item.prop!], item.enum!, item.searchProps)
										: formatValue(scope.row[item.prop!])
								}}
							</el-tag>
							<!-- 文字（自带格式化内容） -->
							<span v-else>
								{{
									item.enum?.length
										? filterEnum(scope.row[item.prop!], item.enum!, item.searchProps)
										: formatValue(scope.row[item.prop!])
								}}
							</span>
						</slot>
					</template>
				</el-table-column>
			</template>
			<template #empty>
				<div class="table-empty">
					<img src="@/assets/images/notData.png" alt="notData" />
					<div>暂无数据</div>
				</div>
			</template>
		</el-table>
		<!-- 分页组件 -->
		<Pagination
			v-if="pagination"
			:pageable="pageable"
			:handleSizeChange="handleSizeChange"
			:handleCurrentChange="handleCurrentChange"
		/>
	</div>
	<!-- 列设置 -->
	<PopoverColSetting
		v-if="toolButton"
		ref="popovercolRef"
		:buttonRef="popoverColSettingRef"
		v-model="tableColumns"
		@table-ref-do-layout="tableRefDoLayout"
	/>
</template>

<script setup lang="ts" name="proTable">
import { ref, watch, computed, onBeforeMount } from 'vue'
import { ElTable } from 'element-plus'
import { useTable } from '@/hooks/useTable'
import { useSelection } from '@/hooks/useSelection'
import { Refresh, Printer, Operation, Search } from '@element-plus/icons-vue'
import { ColumnProps } from '@/components/ProTable/interface'
import { filterEnum, formatValue } from '@/utils/util'
// import SearchForm from '@/components/SearchForm/index.vue'
import ProSearch from '@/components/ProSearch/index.vue'
import Pagination from './components/Pagination.vue'
import PopoverColSetting from './components/PopoverColSetting.vue'
import printJS from 'print-js'

// 表格 DOM 元素
const tableRef = ref<InstanceType<typeof ElTable>>()

// 是否显示搜索模块
const isShowSearch = ref<boolean>(true)

interface ProTableProps {
	columns: Partial<ColumnProps>[] // 列配置项
	requestApi: (params: any) => Promise<any> // 请求表格数据的api ==> 必传
	dataCallback?: (data: any) => any // 返回数据的回调函数，可以对数据进行处理
	blocked?: boolean // 是否阻塞接口请求（默认为false）
	pagination?: boolean // 是否需要分页组件 ==> 非必传（默认为true）
	initParam?: any // 初始化请求参数 ==> 非必传（默认为{}，必须是 reactive 包裹的）
	border?: boolean // 表格是否显示边框 ==> 非必传（默认为true）
	stripe?: boolean // 是否带斑马纹表格 ==> 非必传（默认为false）
	toolButton?: boolean // 是否显示表格功能按钮 ==> 非必传（默认为true）
	childrenName?: string // 当数据存在 children 时，指定 children key 名字 ==> 非必传（默认为"children"）
	selectId?: string // 当表格可以多选时，所指定的 id ==> 非必传（默认为 id）
	printTitle?: string // 打印标题 ==> 非必传（默认为 列表）
	isFullTable?: boolean // 外层是否无高度容器，需要表格自行撑开无数据时的情况
}

// 接受父组件参数，配置默认值
const props = withDefaults(defineProps<ProTableProps>(), {
	blocked: false,
	columns: () => [],
	pagination: true,
	initParam: {},
	border: true,
	stripe: false,
	toolButton: true,
	childrenName: 'children',
	selectId: 'id',
	printTitle: '列表',
	isFullTable: false
})

// 表格多选 Hooks
const { selectionChange, getRowKeys, selectedList, selectedListIds, isSelected } = useSelection(props.selectId)

// 清空选中数据列表
const clearSelection = () => tableRef.value!.clearSelection()

// 表格操作 Hooks
const {
	loading,
	tableData,
	pageable,
	searchParam,
	searchInitParam,
	getTableList,
	search,
	reset,
	handleSizeChange,
	handleCurrentChange
} = useTable(props.requestApi, props.initParam, props.pagination, props.dataCallback, props.blocked)

// 监听页面 initParam 改化，重新获取表格数据
watch(
	() => props.initParam,
	() => {
		getTableList()
	},
	{ deep: true }
)

// 表格列配置项处理（添加 isShow 属性，控制显示/隐藏）
const tableColumns = ref<Partial<ColumnProps>[]>(
	props.columns.map(item => {
		return {
			isShow: true,
			colShow: true,
			...item
		}
	})
)

// 排序跟新列表
const tableRefDoLayout = () => {
	tableRef.value?.doLayout()
}

let searchColumns = ref<Partial<ColumnProps>[]>([])
onBeforeMount(async () => {
	// 如果当前 enum 为后台数据 需要请求数据，则调用该请求接口，获取enum数据
	for (const item of tableColumns.value) {
		if (item.enum && typeof item.enum === 'function') {
			const data = await item.enum()
			item.enum = data
		}
	}
	// 过滤需要搜索的配置项
	searchColumns.value = tableColumns.value.filter(item => item.search)
	// 设置搜索表单的默认值
	searchColumns.value.forEach(column => {
		if (column.searchInitParam !== undefined && column.searchInitParam !== null) {
			searchInitParam.value[column.prop!] = column.searchInitParam
		}
	})
})
// 设置
const popoverColSettingRef = ref()

// 列设置
const popovercolRef = ref()
// 过滤掉不需要设置显隐的列
// 排序
const colSetting = () => {
	let itmeL: Array<Partial<ColumnProps>> = []
	let itmeCen: Array<Partial<ColumnProps>> = []
	let itmeR: Array<Partial<ColumnProps>> = []
	tableColumns.value.forEach((element: Partial<ColumnProps>) => {
		if (
			element.type !== 'selection' &&
			element.type !== 'index' &&
			element.type !== 'expand' &&
			element.prop !== 'operation' &&
			element.isShow !== false &&
			element.colShow !== false
		) {
			if (element?.fixed === 'left') {
				itmeL.push(element)
			} else if (element?.fixed === 'right') {
				itmeR.push(element)
			} else {
				itmeCen.push(element)
			}
		}
	})

	return [...itmeL, ...itmeCen, ...itmeR]
}

const openPopoverColSetting = () => {
	popovercolRef.value.onClickOutside()
}
// 处理打印数据（把后台返回的字典值根据 enum 做转换）
const printData = computed(() => {
	let printDataList = JSON.parse(JSON.stringify(selectedList.value.length ? selectedList.value : tableData.value))
	let colEnumList = colSetting().filter(item => item.enum)
	colEnumList.forEach(colItem => {
		printDataList.forEach((tableItem: any) => {
			tableItem[colItem.prop!] = filterEnum(tableItem[colItem.prop!], colItem.enum, colItem.searchProps)
		})
	})
	return printDataList
})

// 打印表格数据
const handlePrint = () => {
	printJS({
		printable: printData.value,
		header: `<div style="display: flex;flex-direction: column;text-align: center"><h2>${props.printTitle}</h2></div>`,
		properties: colSetting()
			.filter(item => item.isShow && item.colShow)
			.map((item: Partial<ColumnProps>) => {
				return {
					field: item.prop,
					displayName: item.label
				}
			}),
		type: 'json',
		gridHeaderStyle:
			'border: 1px solid #ebeef5;height: 45px;font-size: 14px;color: #232425;text-align: center;background-color: #fafafa;',
		gridStyle: 'border: 1px solid #ebeef5;height: 40px;font-size: 14px;color: #494b4e;text-align: center'
	})
}

// 暴露给父组件的参数和方法(外部需要什么，都可以从这里暴露出去)
defineExpose({ searchParam, getTableList, clearSelection, search, selectedList })
</script>
